package pers.cfeng.server.dataManage.pageIndex;

import pers.cfeng.server.dataManage.pageCache.PageCacheConstant;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 页面大小是固定的， 普通的页面一般就是连续的进行数据插入，记录了一个FSO
 * 但是一个数据库文件中有从1到n多个页面，要想快速找到合适的页面进行数据的插入
 * 不可能直接扫描磁盘文件或者整个Cache
 *  记录的是页号 + 页内空闲空间
 *
 * 使用时取出，所以同一个页面不能同时并发写
 */

public class PageIndex {
    //页面索引，一页的空间划分为40块
    private static final int INTERVALS_NO = 40;
    private static final int THRESHOLD = PageCacheConstant.PAGE_SIZE/INTERVALS_NO;

    private Lock lock;
    private List<PageInfo>[] lists; //存储的页面空间信息

    @SuppressWarnings("unchecked")
    public PageIndex() {
        lock = new ReentrantLock();
        lists = new List[INTERVALS_NO + 1];//40个list
        for(int i = 0; i < INTERVALS_NO + 1; i ++) {
            lists[i] = new ArrayList<>();
        }
    }

    public void add(int pageNo, int freeSpace) {
        lock.lock();
        try {
            int num = freeSpace/THRESHOLD; //该页面剩余
            lists[num].add(new PageInfo(pageNo, freeSpace));
        }  finally {
            lock.unlock();
        }
    }

    /**
     * 选择使用时只要空闲空间为给定块即可
     */
    public PageInfo select(int spaceSize) {
        lock.lock();
        try {
            int number = spaceSize/THRESHOLD;
            if(number < INTERVALS_NO)
                number ++;  //向上取整
            while(number <= INTERVALS_NO) {
                if(lists[number].size() == 0) {
                    number ++;
                    continue;
                }
                //写的时候space改变，取出所以不能多线程同时写一个页面
                return lists[number].remove(0);
            }
            return null;
        } finally {
            lock.unlock();
        }
    }
}
